These are my solutions for TypeHero's 2023 Advent of TypeScript. I'll update this post as new ones are released.
Day One
type SantasFavoriteCookies = "ginger-bread" | "chocolate-chip";
Day Two
type CookieSurveyInput<T> = keyof T;
Day Three
type GiftWrapper<A, B, C> = {
present: A;
from: B;
to: C;
};
Day Four
type Address = { address: string; city: string };
type PresentDeliveryList<T> = {
[Property in keyof T]: Address;
};
Day Five
type SantasList<
BadList extends readonly any[],
GoodList extends readonly any[]
> = [...BadList, ...GoodList];
Day Six
type FilterChildrenBy<List, Filter> = Exclude<List, Filter>;
Day Seven
type AppendGood<ListT> = {
[Property in keyof ListT as `good_${string & Property}`]: ListT[Property];
};
Day Eight
type RemoveNaughtyChildren<T> = Omit<T, `naughty_${string}`>;
Day Nine
type Reverse<T extends string> = T extends `${infer Head}${infer Tail}`
? `${Reverse<Tail>}${Head}`
: "";
Day Ten
type StreetSuffixTester<
TString extends string,
TSuffix extends string
> = TString extends `${string}${TSuffix}` ? true : false;
Day Eleven
type SantaListProtector<T> = T extends Record<string, unknown> | Array<unknown>
? {
readonly [K in keyof T]: SantaListProtector<T[K]>;
}
: T;
Day Twelve
type FindSanta<TForest extends ("đ
đŧ" | "đ")[]> = TForest extends [
...infer Head extends ("đ
đŧ" | "đ")[],
infer Tail
]
? Tail extends "đ
đŧ"
? Head["length"]
: FindSanta<Head>
: never;
Day Thirteen
type DayCounter<FromT extends number, ToT extends number> = [
...DropHead<FromT, Tail<Arr<ToT>>>,
ToT
][number];
type Arr<N extends number, T extends any[] = []> = T["length"] extends N
? T
: Arr<N, [...T, T["length"]]>;
type DropHead<N extends number, T extends number[]> = T extends [
infer Head,
...infer Tail extends number[]
]
? N extends Head
? T
: DropHead<N, Tail>
: [];
type Tail<T extends any[]> = T extends [infer Head, ...infer Tail] ? Tail : [];
Day Fourteen
type DecipherNaughtyList<ListT extends string> = ListToArray<ListT>[number];
type ListToArray<ListT extends string> =
ListT extends `${infer Name}/${infer Rest}`
? [Name, ...ListToArray<Rest>]
: [ListT];
Day Fifteen
type BoxToys<S, N extends number, A extends any[] = []> = N extends A["length"]
? A
: BoxToys<S, N, [S, ...A]>;
Day Sixteen
type FindSanta<TForest extends string[][]> = TForest extends [
...infer Head extends string[][],
infer Tail extends string[]
]
? FindSantaArray<Tail> extends never
? FindSanta<Head>
: [Head["length"], FindSantaArray<Tail>]
: unknown;
type FindSantaArray<TForest extends string[]> = TForest extends [
...infer Head extends string[],
infer Tail
]
? Tail extends "đ
đŧ"
? Head["length"]
: FindSantaArray<Head>
: never;
Day Seventeen
type RockPaperScissors = "đđģ" | "đđž" | "âđŊ";
type WhoWins<
TOpponent extends RockPaperScissors,
TYou extends RockPaperScissors
> = TOpponent extends TYou
? "draw"
: TOpponent extends "đđģ"
? TYou extends "đđž"
? "win"
: "lose"
: TOpponent extends "đđž"
? TYou extends "đđģ"
? "lose"
: "win"
: TYou extends "đđģ"
? "win"
: "lose";
Day Eighteen
type Count<ToySack, Toy> = Filter<ToySack, Toy>["length"];
type Filter<ToySack, Toy> = ToySack extends [infer Head, ...infer Tail]
? Head extends Toy
? [Head, ...Filter<Tail, Toy>]
: Filter<Tail, Toy>
: [];
Day Nineteen
type NextItem = {
"đš": "đ˛";
"đ˛": "đ´";
"đ´": "đ";
"đ": "đš";
};
type Rebuild<List> = Flatten<RebuildArray<List>>;
type RebuildArray<List, Item extends keyof NextItem = "đš"> = List extends [
infer Head extends number,
...infer Tail
]
? [Repeat<Item, Head>, ...RebuildArray<Tail, NextItem[Item]>]
: [];
type Repeat<S, N extends number, A extends any[] = []> = N extends A["length"]
? A
: Repeat<S, N, [S, ...A]>;
type Flatten<T> = T extends [infer Head extends any[], ...infer Tail]
? [...Head, ...Flatten<Tail>]
: [];
Day Twenty
type Letters = {
A: ["âââ ", "âââ ", "â â "];
B: ["âââ ", "âââ ", "ââ "];
C: ["âââ ", "â ââ", "âââ "];
E: ["âââ ", "âââ ", "âââ "];
H: ["â â ", "âââ ", "â â "];
I: ["â ", "â ", "â "];
M: ["âââââ ", "â â â ", "â âââ "];
N: ["ââââ ", "â ââ ", "â ââ "];
P: ["âââ ", "âââ ", "â ââ"];
R: ["âââ ", "âââ ", "â â "];
S: ["âââ ", "âââ ", "âââ "];
T: ["âââ ", "ââ â", "ââ â"];
Y: ["â â ", "âââ ", "ââ â"];
W: ["â âââ ", "âââââ ", "â â â "];
" ": ["â", "â", "â"];
":": ["#", "â", "#"];
"*": ["â", "#", "â"];
};
type ToAsciiArt<S extends string> = Flatten<ToAsciiArtLines<S>>;
type ToAsciiArtLines<S extends string> = S extends `${infer H}\n${infer T}`
? [ToAsciiArtLine<H>, ...ToAsciiArtLines<T>]
: [ToAsciiArtLine<S>];
type ToAsciiArtLine<S extends string> = Joiner<ToAsciiArray<S>>;
type ToAsciiArray<S extends string> =
Uppercase<S> extends `${infer H extends keyof Letters}${infer T}`
? [Letters[H], ...ToAsciiArray<T>]
: [];
type Joiner<
L extends [string, string, string][],
Tops extends string = "",
Middles extends string = "",
Bottoms extends string = ""
> = L extends [
[
infer Top extends string,
infer Middle extends string,
infer Bottom extends string
],
...infer Tail extends [string, string, string][]
]
? Joiner<Tail, `${Tops}${Top}`, `${Middles}${Middle}`, `${Bottoms}${Bottom}`>
: [Tops, Middles, Bottoms];
type Flatten<T> = T extends [infer Head extends any[], ...infer Tail]
? [...Head, ...Flatten<Tail>]
: [];
Day Twenty-One
type TicTacToeChip = "â" | "â";
type TicTacToeEndState = "â Won" | "â Won" | "Draw";
type TicTacToeState = TicTacToeChip | TicTacToeEndState;
type TicTacToeEmptyCell = " ";
type TicTacToeCell = TicTacToeChip | TicTacToeEmptyCell;
type TicTacToeYPositions = "top" | "middle" | "bottom";
type TicTacToeXPositions = "left" | "center" | "right";
type TicTacToePositions = `${TicTacToeYPositions}-${TicTacToeXPositions}`;
type TicTacToeBoard = TicTacToeCell[][];
type TicTacToeGame = {
board: TicTacToeBoard;
state: TicTacToeState;
};
type EmptyBoard = [[" ", " ", " "], [" ", " ", " "], [" ", " ", " "]];
type NewGame = {
board: EmptyBoard;
state: "â";
};
type TicTacToe<
Game extends TicTacToeGame,
Move extends TicTacToePositions
> = IsMoveInvalid<Game, Move> extends true
? Game
: {
board: NextBoard<Game, Move>;
state: VerifyState<NextBoard<Game, Move>, Game["state"]>;
};
type IsMoveInvalid<Game extends TicTacToeGame, Move> = GetCell<
Game,
Move
> extends TicTacToeEmptyCell
? false
: true;
type GetCell<Game extends TicTacToeGame, Move> = Game["board"][PositionYToIndex<
MoveY<Move>
>][PositionXToIndex<MoveX<Move>>];
type PositionYToIndex<Position> = Position extends "top"
? 0
: Position extends "middle"
? 1
: 2;
type PositionXToIndex<Position> = Position extends "left"
? 0
: Position extends "center"
? 1
: 2;
type NextBoard<
Game extends TicTacToeGame,
Move extends TicTacToePositions
> = Game["state"] extends TicTacToeChip
? UpdateBoard<Game["board"], MoveY<Move>, MoveX<Move>, Game["state"]>
: Game["board"];
type MoveX<Move> = Move extends `${string}-${infer X}` ? X : unknown;
type MoveY<Move> =
Move extends `${infer Y extends TicTacToeYPositions}-${string}` ? Y : unknown;
type VerifyState<
Board extends TicTacToeBoard,
Chip extends TicTacToeState
> = SomeWin<Board, Chip> extends true
? `${Chip} Won`
: SomeDraw<Board> extends true
? `Draw`
: NextChip<Chip>;
type SomeWin<Board extends TicTacToeBoard, Chip> = SomeLineWin<
Board,
Chip
> extends true
? true
: SomeColumnWin<Board, Chip> extends true
? true
: SomeDiagonalWin<Board, Chip> extends true
? true
: false;
type SomeDraw<Board extends TicTacToeBoard> = IsLineFull<Board[0]> extends false
? false
: IsLineFull<Board[1]> extends false
? false
: IsLineFull<Board[2]> extends false
? false
: true;
type IsLineFull<Line extends TicTacToeCell[]> =
Line[0] extends TicTacToeEmptyCell
? false
: Line[1] extends TicTacToeEmptyCell
? false
: Line[2] extends TicTacToeEmptyCell
? false
: true;
type SomeLineWin<Board extends TicTacToeBoard, Chip> = LineWin<
Board[0],
Chip
> extends true
? true
: LineWin<Board[1], Chip> extends true
? true
: LineWin<Board[2], Chip> extends true
? true
: false;
type LineWin<Line extends TicTacToeCell[], Chip> = Line[0] extends Chip
? Line[1] extends Chip
? Line[2] extends Chip
? true
: false
: false
: false;
type SomeColumnWin<Board extends TicTacToeBoard, Chip> = LineWin<
[Board[0][0], Board[1][0], Board[2][0]],
Chip
> extends true
? true
: LineWin<[Board[0][1], Board[1][1], Board[2][1]], Chip> extends true
? true
: LineWin<[Board[0][2], Board[1][2], Board[2][2]], Chip>;
type SomeDiagonalWin<Board extends TicTacToeBoard, Chip> = LineWin<
[Board[0][0], Board[1][1], Board[2][2]],
Chip
> extends true
? true
: LineWin<[Board[2][0], Board[1][1], Board[0][2]], Chip>;
type UpdateBoard<Board extends TicTacToeBoard, Y, X, Chip> = Y extends "top"
? [UpdateLine<Board[0], X, Chip>, Board[1], Board[2]]
: Y extends "middle"
? [Board[0], UpdateLine<Board[1], X, Chip>, Board[2]]
: [Board[0], Board[1], UpdateLine<Board[2], X, Chip>];
type UpdateLine<Cells extends TicTacToeCell[], X, Chip> = X extends "left"
? Cells[0] extends TicTacToeEmptyCell
? [Chip, Cells[1], Cells[2]]
: Cells
: X extends "center"
? Cells[1] extends TicTacToeEmptyCell
? [Cells[0], Chip, Cells[2]]
: Cells
: Cells[2] extends TicTacToeEmptyCell
? [Cells[0], Cells[1], Chip]
: Cells;
type NextChip<State> = State extends "â" ? "â" : "â";
Day Twenty-Two
/** because "dashing" implies speed */
type Dasher = "đ¨";
/** representing dancing or grace */
type Dancer = "đ";
/** a deer, prancing */
type Prancer = "đĻ";
/** a star for the dazzling, slightly mischievous Vixen */
type Vixen = "đ";
/** for the celestial body that shares its name */
type Comet = "âī¸";
/** symbolizing love, as Cupid is the god of love */
type Cupid = "â¤ī¸";
/** representing thunder, as "Donner" means thunder in German */
type Donner = "đŠī¸";
/** meaning lightning in German, hence the lightning bolt */
type Blitzen = "âĄ";
/** for his famous red nose */
type Rudolph = "đ´";
type Reindeer =
| Dasher
| Dancer
| Prancer
| Vixen
| Comet
| Cupid
| Donner
| Blitzen
| Rudolph;
type Validate<Board extends Reindeer[][][]> = And<
[ValidateRows<Board>, ValidateColumns<Board>, ValidateRegions<Board>]
>;
type And<Predicates extends boolean[]> = Predicates extends [
infer Head,
...infer Tail extends boolean[]
]
? Head extends true
? And<Tail>
: false
: true;
type ValidateRows<Board extends Reindeer[][][]> = And<
[
NoRepeats<GetRow<Board, 0>>,
NoRepeats<GetRow<Board, 1>>,
NoRepeats<GetRow<Board, 2>>,
NoRepeats<GetRow<Board, 3>>,
NoRepeats<GetRow<Board, 4>>,
NoRepeats<GetRow<Board, 5>>,
NoRepeats<GetRow<Board, 6>>,
NoRepeats<GetRow<Board, 7>>,
NoRepeats<GetRow<Board, 8>>
]
>;
type ValidateColumns<Board extends Reindeer[][][]> = And<
[
NoRepeats<GetColumn<Board, 0, 0>>,
NoRepeats<GetColumn<Board, 0, 1>>,
NoRepeats<GetColumn<Board, 0, 2>>,
NoRepeats<GetColumn<Board, 1, 0>>,
NoRepeats<GetColumn<Board, 1, 1>>,
NoRepeats<GetColumn<Board, 1, 2>>,
NoRepeats<GetColumn<Board, 2, 0>>,
NoRepeats<GetColumn<Board, 2, 1>>,
NoRepeats<GetColumn<Board, 2, 2>>
]
>;
type ValidateRegions<Board extends Reindeer[][][]> = And<
[
NoRepeats<[...Board[0][0], ...Board[1][0], ...Board[2][0]]>,
NoRepeats<[...Board[0][1], ...Board[1][1], ...Board[2][1]]>,
NoRepeats<[...Board[0][2], ...Board[1][2], ...Board[2][2]]>,
NoRepeats<[...Board[3][0], ...Board[4][0], ...Board[5][0]]>,
NoRepeats<[...Board[3][1], ...Board[4][1], ...Board[5][1]]>,
NoRepeats<[...Board[3][2], ...Board[4][2], ...Board[5][2]]>,
NoRepeats<[...Board[6][0], ...Board[7][0], ...Board[8][0]]>,
NoRepeats<[...Board[6][1], ...Board[7][1], ...Board[8][1]]>,
NoRepeats<[...Board[6][2], ...Board[7][2], ...Board[8][2]]>
]
>;
type NoRepeats<List extends Reindeer[]> = And<
[
NoRepeatItem<List, Dasher>,
NoRepeatItem<List, Dancer>,
NoRepeatItem<List, Prancer>,
NoRepeatItem<List, Vixen>,
NoRepeatItem<List, Comet>,
NoRepeatItem<List, Cupid>,
NoRepeatItem<List, Donner>,
NoRepeatItem<List, Blitzen>,
NoRepeatItem<List, Rudolph>
]
>;
type NoRepeatItem<List extends Reindeer[], Item extends Reindeer> = Filter<
List,
Item
>["length"] extends 8
? true
: false;
type Filter<List, Item> = List extends [infer Head, ...infer Tail]
? Head extends Item
? Filter<Tail, Item>
: [Head, ...Filter<Tail, Item>]
: [];
type GetRow<Board extends Reindeer[][][], RowIndex extends number> = [
...Board[RowIndex][0],
...Board[RowIndex][1],
...Board[RowIndex][2]
];
type GetColumn<
Board extends Reindeer[][][],
GroupIndex extends number,
ColumnIndex extends number
> = [
Board[0][GroupIndex][ColumnIndex],
Board[1][GroupIndex][ColumnIndex],
Board[2][GroupIndex][ColumnIndex],
Board[3][GroupIndex][ColumnIndex],
Board[4][GroupIndex][ColumnIndex],
Board[5][GroupIndex][ColumnIndex],
Board[6][GroupIndex][ColumnIndex],
Board[7][GroupIndex][ColumnIndex],
Board[8][GroupIndex][ColumnIndex]
];